package jwf.fcgi;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class FCGXRequest {

    private Socket socket;
    private boolean isBeginProcessed;
    private int requestID;
    private boolean keepConnection;
    private int role;
    private int appStatus;
    private int numWriters;
    private BufferedInputStream inStream;
    private BufferedOutputStream outStream;
    private FCGXOutputStream errStream;
    private Map<String, String> params;
    private Set<String> stdin;

    public FCGXRequest(Socket sock) throws IOException {
	socket = sock;
	// inStream = new FCGXInputStream(socket.getInputStream());
	inStream = new BufferedInputStream(socket.getInputStream(), 8192);
	// outStream = new FCGXOutputStream(socket.getOutputStream());
	// outStream = new BufferedOutputStream(socket.getOutputStream());
	outStream = new BufferedOutputStream(socket.getOutputStream(), 8192);
	// errStream = new FCGXOutputStream(socket.getOutputStream());
	params = new HashMap<String, String>();
	stdin = new HashSet<String>();
    }

    public boolean init() {
	FCGXHeader fcgxHeader = null;
	try {
	    while ((fcgxHeader = FCGXHeader.readHeader(inStream)) != null) {
		switch (fcgxHeader.getType()) {
		case FCGXConstants.FCGIBeginRequest:
		    FCGXBeginRequestBody body = FCGXBeginRequestBody
			    .makeBeginRequestBody(inStream);
		    role = body.getRole();
		    requestID = fcgxHeader.getRequestId();
		    break;

		case FCGXConstants.FCGIParams:
		    FCGXParam fcgxParam = FCGXParam.makeParams(inStream,
			    fcgxHeader);
		    params.putAll(fcgxParam.getParams());
		    break;

		case FCGXConstants.FCGIStdin:
		    stdin.add(FCGXStdin.makeStdin(inStream, fcgxHeader));
		    break;
		default:
		    break;
		}
		fcgxHeader = null;
	    }
	} catch (IOException e) {
	    e.printStackTrace();
	    return false;
	}
	return true;
    }

    public void putStr(String str) throws IOException {
	if (!"".equals(str)) {
	    int contentLength = str.getBytes().length;
	    int paddingLength = FCGXConstants.FCGIHeaderLen - contentLength
		    % FCGXConstants.FCGIHeaderLen;
	    byte[] header = FCGXHeader.makeHeader(FCGXConstants.FCGIStdout,
		    requestID, contentLength, paddingLength, 0);
	    outStream.write(header);
	    outStream.write(str.getBytes());
	    if (paddingLength > 0) {
		byte[] paddingData = new byte[paddingLength];
		outStream.write(paddingData);
	    }
	    byte[] endOut = FCGXHeader.makeHeader(FCGXConstants.FCGIStdout,
		    requestID, 0, 0, 0);
	    outStream.write(endOut);
	}
    }

    private void writeCloseRecord() throws IOException {
	byte[] endRequestHeader = FCGXHeader.makeHeader(
		FCGXConstants.FCGIEndRequest, requestID,
		FCGXConstants.FCGIHeaderLen, 0, 0);
	byte[] endRequestBody = FCGXEndRequestBody.makeEndRequestBody(
		appStatus, FCGXConstants.FCGIRequestComplete);
	outStream.write(endRequestHeader);
	outStream.write(endRequestBody);
    }

    public void destroy() {
	if (socket != null) {
	    try {
		writeCloseRecord();
		outStream.flush();
		inStream.close();
		outStream.close();
		socket.close();
	    } catch (IOException e) {
		e.printStackTrace();
	    }
	}
    }

    public Map<String, String> getParams() {
	return params;
    }

    public Set<String> getStdin() {
	return stdin;
    }

}
